import timber.util.Config;
import timber.browser.JS;

class timber.controller.App
{
	private static var instance = null;
	private var location;
	private var locationstring;
	private var $request;
	private var delegates;
	private var transitionDelegates;
	private var currentlyInTransition;
	private var transitionDestinationPathString;
	private var transitionDestinationPath;
	
// ===========================================================
// - CONSTRUCTOR
// ===========================================================
	private function App() {
		AsBroadcaster.initialize(this);
		location = new Array();
		$request = {};
		delegates = new Array();
		transitionDelegates = new Array();
		currentlyInTransition = false;
		transitionDestinationPath = false;
		transitionDestinationPathString = false;
		
		// install the app in global
		_global.App = timber.controller.App;
	}

// ===========================================================
// - GET SINGLETON INSTANCE
// ===========================================================
	public static function getInstance() {
		if (instance == null) {
			App.instance = new App();
		}
		return App.instance;
	}

// ===========================================================
// - LOAD CONFIG
// ===========================================================
	public static function init() {
		// load the config file using the config var
		// if the config var isn't set use the default location
		if (!_root.config) _root.config = '_xml/config.xml';
		var conf = new Config(_root.config, App.getInstance().didLoadConfig);
	}


// ===========================================================
// - BROADCAST MESSAGES
// ===========================================================
	private function didLoadConfig() {
		App.getInstance().broadcastMessage("onLoadConfig");
	}

	private function didNavigate() {
		App.getInstance().broadcastMessage("onNavigate", App.getLocation());
	}



// ===========================================================
// - TRANSITIONS
// ===========================================================
	public static function outTransitionComplete() {
		var a = App.getInstance();
		a.currentlyInTransition = false;
		a.executeNavigation(a.transitionDestinationPath, a.transitionDestinationPathString);
	}

	// MESSAGES
	private function onOutTransition(oldpath, newpath) {
		App.getInstance().broadcastMessage("onOutTransition", oldpath, newpath);
	}

	private function onOutTransitionComplete() {
		App.getInstance().broadcastMessage("onOutTransitionComplete");
	}





// ===========================================================
// - ACCESSORS
// ===========================================================
	public static function getLocation() {
		return App.getInstance().location;
	}

	public static function get request() {
		return App.getInstance().$request;		
	}

	public static function getDelegates() {
		return App.getInstance().delegates;
	}

	public static function setDelegate(del) {
		if (del instanceof Array) {
			App.getInstance().delegates = del;
		} else {
			App.getInstance().delegates = [del];
		}
	}

	public static function addDelegate(del) {
		App.getInstance().delegates.push(del);
	}

	
	// trans delegate
	public static function getTransitionDelegates() {
		return App.getInstance().transitionDelegates;
	}

	public static function setTransitionDelegate(del) {
		if (del instanceof Array) {
			App.getInstance().transitionDelegates = del;
		} else {
			App.getInstance().transitionDelegates = [del];
		}
	}

	public static function addTransitionDelegate(del) {
		App.getInstance().transitionDelegates.push(del);
	}


	// listeners
	public static function addListener(val) {
		App.getInstance().addListener(val);
	}

	public static function isTransitioning() {
		return App.getInstance().currentlyInTransition;
	}

// ===========================================================
// - DO THE NAVIGATION
// ===========================================================
	public static function navigate(val, target) {
		var a = App.getInstance();
		var newpath;
		
		// make sure the path is a string
		val = val.toString();
		
		// save full string
		var requestedurl = val;
		
		// see if this is a javascript or a http link
		var scheme = val.split(':')[0];
		if (scheme == 'http') {
			// check with the delegate
			if (!(App.getDelegates().length > 0) || !a.checkDelegates(val, 'shouldNavigate')) {
				if (typeof target != 'undefined') {
					getURL(val, target);
				} else {
					getURL(val);
				}
				return;
			}
		} else if (scheme == 'javascript') {
			// check with the delegate
			if (!App.getDelegates() || !a.checkDelegates(val, 'shouldNavigate')) {
				JS.call(val);
				return;
			}
		}

		// see if there are query params to grab
		// these are stored in App.request
		// first clear the old
		a.$request = {};
		if (val.indexOf('?') > -1) {
			var parts = val.split('?');
			val = parts[0];
			
			// now parse the query into k=v pairs
			var kvp = parts[1].split('&');
			var kv;
			for (var i=0; i<kvp.length; i++ ) {
				kv = kvp[i].split('=');
				a.$request[kv[0]] = kv[1];
			}
		}

		// check if the path is local
		if (val.charAt(0) == '.') {
			val = a.location + val.substring(1);
		}

		if (val.charAt(0) == '/') {
			newpath = val.substring(1).split('/');
		} else {

			// if we got this far it isn't abs
			var lparts = val.split('/');
		
			// either way we need everything but the last
			newpath = a.location;
			newpath.pop();
		
			// make sure the path doesn't contain any relative elements (..)
			// if it does start with the current path-1 and pop one off for each ..
			// in the parts array
			var c = -1;
			if (val.indexOf('../', c) > -1) {
				for (var i=0; i<lparts.length; i++) {
					if (lparts[i] == '..') {
						newpath.pop();
					} else {
						newpath.push(lparts[i]);
					}
				}
			} else {
				// if it isn't a rel path, just add the new path to the end of the old
				newpath = newpath.concat(lparts);
			}			
		}
		
		// skip if it's where we are
		if (a.locationstring == requestedurl) return false;

		// check with the delegate
		if (!a.checkDelegates(newpath, 'shouldNavigate')) {
			return false;
		}
		
		// check if we need a page transition
		if (a.checkTransitionDelegates(newpath, 'requiresOutTransition')) {
			a.transitionDestinationPath = newpath;
			a.transitionDestinationPathString = requestedurl;
			a.currentlyInTransition = true;
			a.onOutTransition(a.location, newpath);
			return false;
		}
		
		a.executeNavigation(newpath, requestedurl);
		return true;
	}
	
	/**
	* Does the actual updating of the app's location and send navigation event
	*/
	private function executeNavigation(newpath, requestedurl) {
		var a = App.getInstance();
		a.location = newpath;
		a.locationstring = requestedurl;
		a.didNavigate();		
	}
	
	/**
	 Handle errors.
	*/
	public static function error(msg) {
		trace(msg);
	}
	
	/**
	* Checks all delegates to see if the an event should be blocked
	*/
	public function checkDelegates(newpath, method) {
		var l = App.getInstance().delegates.length;
		
		var out = true;
		for (var i=0; i<l; i++) {
			out &= App.getInstance().delegates[i][method](newpath);
		}
		return out;
	}

	/**
	* Checks transition delegates to see if the an event should be blocked
	*/
	public function checkTransitionDelegates(newpath, method) {
		var l = App.getInstance().transitionDelegates.length;
		
		var out = true;
		for (var i=0; i<l; i++) {
			out &= App.getInstance().transitionDelegates[i][method](newpath);
		}
		return out;
	}
}